/* Trabajo Practico 4 (Segunda Entrega)
# Servidor Partida
#
# - Integrantes:
#	- Martinez, Guillermo		DNI: 34510461
#	- Rodriguez, Lucas			DNI: 34265017
*/

#include "include/defines.h"
#include "include/sockets/sockets_s.h"
#include "include/sdl/pantalla.h"
#include "include/s_func/tateti.h"
#include "Mensajes.h"
#include "servPartida.h"

using namespace std;

// Estructura movimiento
s_mov mov;

// Jugador de turno
DatosJugador* JugadorTurno;

// Sockets
int sock1;
int sock2;

// Mensajes inicio
Mensaje msj_inicio;

// Threads IDs
pthread_t tid_socket;

// Semáforos movimientos
sem_t nuevo_mov;
sem_t sem_mov;
sem_t mov_valido;

// Semáforos threads
sem_t mutex_socket_s;
sem_t socket_disp;

// Nombre Jugadores
char nombre1[20];
char nombre2[20];

extern ConfiguracionServidor ConfigServer;

pid_t CrearServidorPartida(DatosJugador* Jugador1,DatosJugador* Jugador2, int Ronda)
{
	pid_t pid;

	if ((pid = fork()) < 0)
		exit(EXIT_FAILURE);
		
	if (pid > 0)
		return pid;
	
	// Ejecuto Servidor Partida
	ServidorPartida(Jugador1,Jugador2,Ronda);
	exit(1);
}

void ServidorPartida(DatosJugador* Jugador1,DatosJugador* Jugador2, int Ronda)
{
	//Codigo Servidor de Partida
	cout << "Partida jugadores: " << Jugador1->Nombre << " vs "<<Jugador2->Nombre <<endl;
	
	signal(SIGTERM,SIG_IGN);
	signal(SIGINT,SIG_IGN);
	signal(SIGALRM,SIG_IGN);
	signal(SIGPIPE,SIG_IGN);
	signal(SIGUSR1,pedido_termino);
	
	// Semáforos
	iniciar_semaforos();
	
	// Mensajes inicio de clientes
	msj_inicio.Tipo=IniciaPartida;
	msj_inicio.Ronda=Ronda;
	
	sock1 = Jugador1->Descriptor;
	sock2 = Jugador2->Descriptor;
	
	// Inicio Thread Socket
	pthread_create(&tid_socket, NULL, thread_socket_s, (void*) &mov);
	usleep(500);
	
	// Enviar Inicio partida a cliente 1
	strcpy(msj_inicio.Nombre,Jugador2->Nombre);
	if(Jugador1->Conectado && !Jugador1->Fantasma)
	{	
		//cout << getpid() << ">> Se envia mensaje a jugador 1" << endl;  
		unlock_socket_s(INICIO,Jugador1);
	}
	sem_wait(&nuevo_mov);
	
	// Enviar Inicio partida a cliente 2
	strcpy(msj_inicio.Nombre,Jugador1->Nombre);
	if(Jugador2->Conectado && !Jugador2->Fantasma)
	{
		//cout << getpid() << ">> Se envia mensaje a jugador 2" << endl;
		unlock_socket_s(INICIO,Jugador2);
	}
	sem_wait(&nuevo_mov);

	/////////Logica partida
	
	EstadoPartida Partida;
	Partida.Jugador1=*Jugador1;
	Partida.Jugador2=*Jugador2;
	Partida.PuntajeJug1=0;
	Partida.PuntajeJug2=0;
	Partida.IdServidorPartida=getpid();
	
	int turno = 1;
	bool GanaJug1;
	
	// A mejor de
	int cant_partidas = 1+ConfigServer.CantJuegosPorPartida/2;

	// Ciclo de una ronda de N partidas (N = cant_partidas)
	while(Partida.PuntajeJug1 < cant_partidas && Partida.PuntajeJug2 < cant_partidas)
	{
		if(Jugador1->Conectado == false || Jugador2->Conectado == false)
		{	// Desconexion?
			if(!Jugador2->Conectado )
			{
				GanaJug1 = true;
				Partida.PuntajeJug1 = ConfigServer.CantJuegosPorPartida;
				Partida.PuntajeJug2 = 0;
				cout << "Ganador: " << Jugador1->Nombre << endl;
			}
			
			else
			{
				GanaJug1 = false;
				Partida.PuntajeJug2 = ConfigServer.CantJuegosPorPartida;
				Partida.PuntajeJug1 = 0;
				cout << "Ganador: " << Jugador2->Nombre << endl;
			}
		}
		
		else            // Lógica juego
			GanaJug1 = JugarTateti(Jugador1, Jugador2,turno);
		
		// Si no hubo una desconexión
		if(Jugador1->Conectado && Jugador2->Conectado)
		{
			if(GanaJug1)
				Partida.PuntajeJug1++;
			else
				Partida.PuntajeJug2++;
			
			// Si no ganó la última partida envío señales
			if(Partida.PuntajeJug1 < cant_partidas && Partida.PuntajeJug2 < cant_partidas)
			{
				// Confirmo ganador y perdedor de partida
				sem_wait(&sem_mov);
				mov.estado = GANADOR;
				mov.accion = INVALIDO;
				mov.n_posicion = mov.posicion = 0;
			
				if(GanaJug1)
				{
					// Confirmación ganador
					unlock_socket_s(CONF,Jugador1);
					sem_wait(&nuevo_mov);
					
					// Confirmación perdedor
					sem_wait(&sem_mov);
					mov.estado = PERDEDOR;
					unlock_socket_s(CONF,Jugador2);
					sem_wait(&nuevo_mov);
				}
			
				else
				{
					// Confirmación ganador
					unlock_socket_s(CONF,Jugador2);
					sem_wait(&nuevo_mov);
					
					// Confirmación perdedor
					sem_wait(&sem_mov);
					mov.estado = PERDEDOR;
					unlock_socket_s(CONF,Jugador1);
					sem_wait(&nuevo_mov);
				}
			}
		}
		                 
		// Cambio turno
		turno == 1 ? turno = 2 : turno = 1;
	}
	
	// Terminó la partida, escribo resultado para server maestro
	EscribirMemoriaCompartida(Partida);
	sleep(1);
	
	exit(0); //Finaliza la partida -- 
}

//Devuelve si gano el Jugador 1
bool JugarTateti(DatosJugador* Jugador1, DatosJugador* Jugador2,int turno)
{
	//Logica de Juego comunicacion con cliente (para cada jueguito del tateti)
	
	int victoriasJ1, victoriasJ2, ganador;
	int fichas[2], tablero[TABLERO][TABLERO];
	int fOrigen, cOrigen, fDestino, cDestino, pos;		// [0]=Servidor | [1]=Jugador1 | [2]=Jugador2
	short int ult_mov = 0;								// Registra último mov
	int value=-1,sock_jugador;
	
	fichas[0] = fichas[1] = 0;
	InicializarTablero(tablero);

	sem_wait(&sem_mov);
	mov.estado = NO_TERMINO;					// No hay ganador todavía
	mov.accion = INVALIDO;						// Si tiene que repetir por inválida
	mov.posicion = ult_mov = 0;
	sem_post(&sem_mov);

	do
	{
		SeleccionaTurno(&turno);
		if(turno == JUG1)
			JugadorTurno = Jugador1;
		else
			JugadorTurno = Jugador2;

		if(fichas[turno-1] < 3)				// Proceso una colocación de ficha
		{
			do
			{
				int auxf = 0;
				
				// Modifico movimiento
				sem_wait(&sem_mov);
				mov.estado = NO_TERMINO;		// No hay ganador todavía
				mov.accion = INVALIDO;			// Si tiene que repetir por inválida
				mov.posicion = ult_mov;
				sem_post(&sem_mov);
				
				// Pido a thread_socket un envío y espero resultado
				unlock_socket_s(SEND,JugadorTurno);
				sem_getvalue(&nuevo_mov,&auxf);
				sem_wait(&nuevo_mov);
				if(JugadorTurno -> Conectado == false)	return false;
				
				BuscarCoordenadas(tablero, mov.posicion, &fDestino, &cDestino);
			}while(!ColocarFicha(tablero, turno, fDestino, cDestino));
			
			// Confirmo movimiento
			sem_wait(&sem_mov);
			mov.accion = VALIDO;
			unlock_socket_s(CONF,JugadorTurno);
			sem_wait(&nuevo_mov);
			if(JugadorTurno -> Conectado == false)	return false;

			// Guardo movimiento para el otro jugador
			sem_wait(&sem_mov);
			ult_mov = mov.posicion;
			sem_post(&sem_mov);
			++fichas[turno-1];

			sleep(1);
		}
		
		else  												// Proceso desplazamiento de ficha
		{
			do
			{
				do
				{
					// Modifico movimiento
					sem_wait(&sem_mov);
					mov.estado = NO_TERMINO;			// No hay ganador todavía
					mov.accion = MOVER;						// Realizar movimiento de piezas
					sem_post(&sem_mov);
					
					// Pido a thread_socket un envío y espero resultado
					unlock_socket_s(SEND,JugadorTurno);
					sem_wait(&nuevo_mov);
					if(JugadorTurno -> Conectado == false)	return false;
					
					BuscarCoordenadas(tablero, mov.posicion, &fOrigen, &cOrigen);
				}while(!EjecutarSeleccion(tablero, turno, fOrigen, cOrigen));
							
				// Envío validez y espero resultado de posición destino
				sem_wait(&sem_mov);
				mov.accion = VALIDO;
				sem_post(&sem_mov);
				unlock_socket_s(SEND,JugadorTurno);
				sem_wait(&nuevo_mov);
				if(JugadorTurno -> Conectado == false)	return false;
				
				BuscarCoordenadas(tablero, mov.n_posicion, &fDestino, &cDestino);
			}while(!EjecutarJugada(tablero, turno, fOrigen, cOrigen, fDestino, cDestino));
			
			tablero[fOrigen][cOrigen] = SIN_FICHA;
			tablero[fDestino][cDestino] = turno;
			
			//Confirmo movimiento
			sem_wait(&sem_mov);
			mov.accion = MOVER_CONF;
			unlock_socket_s(CONF,JugadorTurno);
			sem_wait(&nuevo_mov);
			if(JugadorTurno -> Conectado == false)	return false;
		}

		ganador = HayGanador(tablero, turno);

	}while(ganador != JUG1 && ganador != JUG2);
	
	return ganador == JUG1;
}

void errorServPartida(const char *e)
{
	fcloseall();
	exit(1);
}

void iniciar_semaforos()
{
	// Semáforos
	sem_init(&nuevo_mov,4,0);						// Mutex para inicio partida
	sem_init(&sem_mov,4,1);							// Mutex para mov
	
	// Semáforos sincronización threads
	sem_init(&mutex_socket_s,3,0);
	sem_init(&socket_disp,3,0);
}

void borrar_semaforos()
{
	// Semáforos
	sem_destroy(&nuevo_mov);
	sem_destroy(&sem_mov);
	
	// Semáforos sincronización threads
	sem_destroy(&mutex_socket_s);
	sem_destroy(&socket_disp);
}

void pedido_termino(int sig)
{
	// Termino Thread socket
	pthread_cancel(tid_socket);
	pthread_join(tid_socket, NULL);
	
	// Cierro descriptores
	close(sock1);
	close(sock2);
	
	// Libero recursos
	borrar_semaforos();
	
	exit(0);
}
